#include "MidiFile.h"
#include "Options.h"
#include <iostream>
#include <iomanip>
// #include "midi_merge.h"
#include <stdio.h>
#include <filesystem>

#define QUAN_BPM  100
#define QUAN_TPQ  480

#define MIDI_EVENT_FILE_HEAD \
"\
#ifndef __MIDI_EVENT_LIST_H\n\
#define __MIDI_EVENT_LIST_H\n\
\n\
#ifndef MIDI_DATA_ATTR\r\
#define MIDI_DATA_ATTR\r\
#endif\r\
\n\
typedef struct\n\
{\n\
    unsigned int  tick;\n\
    unsigned char key;\n\
    unsigned char force;\n\
}midi_events_t;\n\
\n\
typedef struct\n\
{\n\
    char time_signature_num;\n\
    char time_signature_den;\n\
    unsigned int event_count;\n\
    midi_events_t events[];\n\
}midi_t;\n\
\n\
"

#define MIDI_EVENT_FILE_END \
"\
\n#endif\n\
"
#define MIDI_EVENT_LIST_NUM_DEFINE \
"#define DRUMMER_MIDI_LIST_MAX"

#define MIDI_EVENT_LIST_ARRAY \
"const midi_t *drummer_midi_list[DRUMMER_MIDI_LIST_MAX] = {\n"

// MIDI音符名数组
char *midi_note_names[] = {
    "C-1", "C#-1/Db-1", "D-1", "D#-1/Eb-1", "E-1", "F-1", "F#-1/Gb-1", "G-1", "G#-1/Ab-1", "A-1", "A#-1/Bb-1", "B-1",
    "C0", "C#0/Db0", "D0", "D#0/Eb0", "E0", "F0", "F#0/Gb0", "G0", "G#0/Ab0", "A0", "A#0/Bb0", "B0",
    "C1", "C#1/Db1", "D1", "D#1/Eb1", "E1", "F1", "F#1/Gb1", "G1", "G#1/Ab1", "A1", "A#1/Bb1", "B1",
    "C2", "C#2/Db2", "D2", "D#2/Eb2", "E2", "F2", "F#2/Gb2", "G2", "G#2/Ab2", "A2", "A#2/Bb2", "B2",
    "C3", "C#3/Db3", "D3", "D#3/Eb3", "E3", "F3", "F#3/Gb3", "G3", "G#3/Ab3", "A3", "A#3/Bb3", "B3",
    "C4", "C#4/Db4", "D4", "D#4/Eb4", "E4", "F4", "F#4/Gb4", "G4", "G#4/Ab4", "A4", "A#4/Bb4", "B4",
    "C5", "C#5/Db5", "D5", "D#5/Eb5", "E5", "F5", "F#5/Gb5", "G5", "G#5/Ab5", "A5", "A#5/Bb5", "B5",
    "C6", "C#6/Db6", "D6", "D#6/Eb6", "E6", "F6", "F#6/Gb6", "G6", "G#6/Ab6", "A6", "A#6/Bb6", "B6",
    "C7", "C#7/Db7", "D7", "D#7/Eb7", "E7", "F7", "F#7/Gb7", "G7", "G#7/Ab7", "A7", "A#7/Bb7", "B7",
    "C8", "C#8/Db8", "D8", "D#8/Eb8", "E8", "F8", "F#8/Gb8", "G8", "G#8/Ab8", "A8", "A#8/Bb8", "B8",
    "C9", "C#9/Db9", "D9", "D#9/Eb9", "E9", "F9", "F#9/Gb9", "G9"
};

#define KEY_MAX_NUM 128

using namespace std;
using namespace smf;
namespace fs = std::filesystem;

typedef struct
{
    unsigned int tick;
    unsigned char event;
    unsigned char key;
    unsigned char force;
}midi_to_c_event;

char outfile_buffer[1024*1024];

void find_directory(const fs::path& path,vector<string*>& file_paths);
void analysis_midi_file(MidiFile& midifile,vector<midi_to_c_event*>& midi_event_list,char *time_signature_num,char *time_signature_den);


static void sort_arrays(unsigned int *buff,uint32_t len)
{
    unsigned int c1,c2,tmp ;
    for(c1=0; c1<len; c1++)
    {
        for(c2=0; c2<len-1-c1; c2++)
        {
            if ( buff[c2] > buff[c2+1] )
            {
                tmp = buff[c2] ;
                buff[c2] = buff[c2+1] ;
                buff[c2+1] = tmp ;
            }
        }
    }
}


int main(int argc, char** argv)
{
    vector<midi_to_c_event*> midi_event_list;
    vector<string*> midi_path_list;
    vector<string> midi_name_list;
    vector<char> midi_note_list;
    char key_used[KEY_MAX_NUM];

    if(argc <= 1)
    {
        return 0;
    }
    find_directory(fs::path(argv[1]),midi_path_list);
    memset(key_used,0,sizeof(key_used));

    int length=0;
    int count;
    ofstream fout;
    string out_file_path = argv[1];
    out_file_path = out_file_path + "\\drummer_midi_list.h";
    //fout.open("drummer_midi_list.h");
    fout.open(out_file_path);
    count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,MIDI_EVENT_FILE_HEAD);
    fout.write(outfile_buffer,count);
    for(int i = 0; i < midi_path_list.size(); i++)
    {
        MidiFile midifile;
        string path = midi_path_list[i]->c_str();
        delete midi_path_list[i];
        if(midifile.read(path))
        {
            midi_event_list.clear();
            cout << "file:" << path << endl;
            char time_signature_num=0;
            char time_signature_den=0;
            analysis_midi_file(midifile,midi_event_list,&time_signature_num,&time_signature_den);
            length = 0;
            if(midi_event_list.size() > 0)
            {
                string fileName = "midi_"+filesystem::path(midifile.getFilename()).stem().string();
                midi_name_list.push_back(fileName);
                count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\nMIDI_DATA_ATTR const midi_t %s={\n",fileName.c_str());
                length += count;
                count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t.time_signature_num = %d,\n",time_signature_num);
                length += count;
                count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t.time_signature_den = %d,\n",time_signature_den);
                length += count;
                count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t.event_count = %d,\n",midi_event_list.size());
                length += count;
                count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t.events = {\n");
                length += count;
                // int isNew;
                for(int a = 0; a < midi_event_list.size(); a++)
                {
                    count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t\t{ %4d, %3d, %3d },\n",midi_event_list[a]->tick,midi_event_list[a]->key,midi_event_list[a]->force);
                    length += count;
                    // isNew = 1;
                    // for(int n=0; n<midi_note_list.size();n++)
                    // {
                    //     if((midi_note_list[n] == midi_event_list[a]->key) || midi_event_list[a]->key == 0)
                    //     {
                    //         isNew = 0;
                    //         break;
                    //     }
                    // }
                    // if(isNew)
                    // {
                    //     midi_note_list.push_back(midi_event_list[a]->key);
                    // }
                    if(midi_event_list[a]->key < KEY_MAX_NUM)
                    {
                        key_used[midi_event_list[a]->key] = 1;
                    }
                    delete midi_event_list[a];
                }
                count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t},\n};\n");
                length += count;
                fout.write(outfile_buffer,length);
            }
        }
    }
    length = 0;
    count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\n%s\t%zd\n",MIDI_EVENT_LIST_NUM_DEFINE,midi_name_list.size());
    length += count;
    count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,MIDI_EVENT_LIST_ARRAY);
    length += count;
    for(int i = 0; i < midi_name_list.size(); i++)
    {
        count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"\t&%s,\n",midi_name_list[i].c_str());
        length += count;
    }
    count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"};\n\n");
    length += count;
    //for(int i = 0; i < midi_note_list.size(); i++)
    for(int i = 0; i < KEY_MAX_NUM; i++)
    {
        //count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"//%d---%s\n",midi_note_list[i],midi_note_names[midi_note_list[i]]);
        if(key_used[i] == 1)
        {
            count = sprintf_s(&outfile_buffer[length],sizeof(outfile_buffer)-length,"//%d---%s\n",i,midi_note_names[i]);
            length += count;
        }
    }
    fout.write(outfile_buffer,length);
    fout.write(MIDI_EVENT_FILE_END,sizeof(MIDI_EVENT_FILE_END)-1);
    fout.close();
    return 0;
}


void find_directory(const fs::path& path,vector<string*>& file_paths)
{
    if (fs::is_directory(path))
    {
        for (const auto &entry : fs::directory_iterator(path))
        {
            if (fs::is_directory(entry.path()))
            {
                // std::cout << "Directory: " << entry.path() << std::endl;
                find_directory(entry.path(),file_paths);
            }
            else
            {
                // std::cout << "File: " << entry.path() << std::endl;
                file_paths.push_back(new string(entry.path().string()));
            }
        }
    }
}

void analysis_midi_file(MidiFile& midifile,vector<midi_to_c_event*>& midi_event_list,char *time_signature_num,char *time_signature_den)
{
    int end_tick = 0;
    int time_signature_top=1000;
    int time_signature_bot=1000;
    int first_tick = -1;
    *time_signature_num = 4;
    *time_signature_den = 4;
    midifile.doTimeAnalysis();
    midifile.linkNotePairs();
    int tracks = midifile.getTrackCount();
    for (int track = 0; track < (tracks-1); track++)
    {
        midifile.mergeTracks(0,1);
    }
    midifile.doTimeAnalysis();
    midifile.linkNotePairs();
    tracks = midifile.getTrackCount();
    cout << "TPQ: " << midifile.getTicksPerQuarterNote() << endl;
    if (tracks > 1)
        cout << "TRACKS: " << tracks << endl;
    for (int track = 0; track < tracks; track++)
    {
        if (tracks > 1)
            cout << "\nTrack " << track << endl;
        cout << "Tick\tSeconds\tDur\tMessage" << endl;
        for (int event = 0; event < midifile[track].size(); event++)
        {
            cout << dec << midifile[track][event].tick;
            cout << '\t' << dec << midifile[track][event].seconds;
            cout << '\t';
            if (midifile[track][event].isNoteOn())
                cout << midifile[track][event].getDurationInSeconds();
            cout << '\t' << hex;
            for (int i = 0; i < midifile[track][event].size(); i++)
                cout << (int)midifile[track][event][i] << ' ';
            cout << endl;
            if(midifile[track][event].isNoteOn())
            {
                midi_to_c_event* e = new midi_to_c_event;
                if(first_tick == -1)
                {
                    first_tick = midifile[track][event].tick;
                }
                e->tick = midifile[track][event].tick - first_tick;
                e->event = midifile[track][event][0];
                e->key = midifile[track][event][1];
                e->force = midifile[track][event][2];
                midi_event_list.push_back(e);
            }
            if(midifile[track][event].isTimeSignature())
            {
                time_signature_top = midifile[track][event][3];
                time_signature_bot = pow(2, midifile[track][event][4]);
            }
        }
    }
    if(time_signature_top > 64)
    {
        time_signature_top = 4;
    }
    if(time_signature_bot > 64)
    {
        time_signature_bot = 4;
    }

    int qtick = time_signature_top * QUAN_TPQ / (time_signature_bot / 4);
    if(midi_event_list.size() > 0)
    {
        end_tick = midi_event_list[midi_event_list.size()-1]->tick;
        if(end_tick % qtick != 0)
        {
            end_tick = end_tick + (qtick - (end_tick % qtick));
        }
    }
    midi_to_c_event* e = new midi_to_c_event;
    e->tick = end_tick;
    e->event = 0xFF;
    e->key = 0;//0x2F
    e->force = 0;
    midi_event_list.push_back(e);
    *time_signature_num = time_signature_top;
    *time_signature_den = time_signature_bot;
}